<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
  Function.prototype.myCall = function(context){
    var context = context || window
    context.fn = this;
    var args = [...arguments].slice(1)
    var result = context.fn(...args)
    delete context.fn;
    return result;
  }
  //applay
  Function.prototype.myApply  = function(context) {
    var context = context || window
    context.fn = this
    let result = arguments[1] ? context.fn(...arguments[1]) :context.fn()
    delete context.fn
    return result
  }
  // bind
  Function.prototype.imitateBind = function (context) {
    // 获取绑定时的传参
	let args = [...arguments].slice(1),
  // 这是伪数组能使用数组的方法
  // var args = Array.prototype.slice.call(arguments, 1),
        // 定义中转构造函数，用于通过原型连接绑定后的函数和调用bind的函数
        F = function () {},
        // 记录调用函数，生成闭包，用于返回函数被调用时执行
        self = this,
        // 定义返回(绑定)函数
        bound = function () {
            // 合并参数，绑定时和调用时分别传入的
            let finalArgs = [...args, ...arguments]
            
            // 改变作用域，注:aplly/call是立即执行函数，即绑定会直接调用
            // 这里之所以要使用instanceof做判断，是要区分是不是new xxx()调用的bind方法
            return self.call((this instanceof F ? this : context), ...finalArgs)
        }
    
    // 将调用函数的原型赋值到中转函数的原型上
    F.prototype = self.prototype
    // 通过原型的方式继承调用函数的原型
    bound.prototype = new F()
    
    return bound
  }
  </script>
</body>
</html>